struct page {
    unsigned long flags;    /* 原子标志，有些可能会被异步更新 */
    
    /*
     * 这个联合体中有5个字(20/40字节)可用。
     * 警告：第一个字的第0位被PageTail()使用。这意味着
     * 该联合体的其他使用者绝不能使用该位，以避免冲突
     * 和误判PageTail()。
     */
    union {
        struct {    /* 页缓存和匿名页 */
            /**
             * @lru: 页换出列表，例如由zone_lru_lock保护的active_list。
             * 有时被页所有者用作通用列表。
             */
            struct list_head lru;
            /* 参见page-flags.h中的PAGE_MAPPING_FLAGS */
            struct address_space *mapping;
            pgoff_t index;      /* 我们在映射中的偏移量 */
            /**
             * @private: 映射专有的不透明数据。
             * 如果PagePrivate，通常用于buffer_heads。
             * 如果PageSwapCache，用于swp_entry_t。
             * 如果PageBuddy，表示伙伴系统中的阶数。
             */
            unsigned long private;
        };
        
        struct {    /* slab, slob和slub内存分配器 */
            union {
                struct list_head slab_list;    /* 复用lru */
                struct {    /* 部分页 */
                    struct page *next;
#ifdef CONFIG_64BIT
                    int pages;    /* 剩余页数 */
                    int pobjects; /* 近似计数 */
#else
                    short int pages;
                    short int pobjects;
#endif
                };
            };
            struct kmem_cache *slab_cache; /* 不用于slob */
            /* 双字边界对齐 */
            void *freelist;     /* 第一个空闲对象 */
            union {
                void *s_mem;    /* slab: 第一个对象 */
                unsigned long counters;       /* SLUB */
                struct {        /* SLUB */
                    unsigned inuse:16;
                    unsigned objects:15;
                    unsigned frozen:1;
                };
            };
        };
        
        struct {    /* 复合页的尾页 */
            unsigned long compound_head;    /* 第0位被设置 */
            
            /* 仅用于第一个尾页 */
            unsigned char compound_dtor;
            unsigned char compound_order;
            atomic_t compound_mapcount;
        };
        
        struct {    /* 复合页的第二个尾页 */
            unsigned long _compound_pad_1;    /* compound_head */
            unsigned long _compound_pad_2;
            struct list_head deferred_list;
        };
        
        struct {    /* 页表页 */
            unsigned long _pt_pad_1;    /* compound_head */
            pgtable_t pmd_huge_pte; /* 由page->ptl保护 */
            unsigned long _pt_pad_2;    /* mapping */
            union {
                struct mm_struct *pt_mm; /* 仅x86 pgds */
                atomic_t pt_frag_refcount; /* powerpc */
            };
#if ALLOC_SPLIT_PTLOCKS
            spinlock_t *ptl;
#else
            spinlock_t ptl;
#endif
        };
        
        struct {    /* ZONE_DEVICE页 */
            /** @pgmap: 指向托管设备页映射 */
            struct dev_pagemap *pgmap;
            unsigned long hmm_data;
            unsigned long _zd_pad_1;    /* 复用mapping */
        };

        /** @rcu_head: 可以用这个通过RCU释放页 */
        struct rcu_head rcu_head;
    };

    union {    /* 这个联合体大小为4字节 */
        /*
         * 如果页可以映射到用户空间，编码该页被页表
         * 引用的次数。
         */
        atomic_t _mapcount;

        /*
         * 如果页既不是PageSlab也不能映射到用户空间，
         * 这里存储的值可能有助于确定该页的用途。
         * 参见page-flags.h中当前存储在这里的页类型列表。
         */
        unsigned int page_type;

        unsigned int active;        /* SLAB */
        int units;            /* SLOB */
    };

    /* 使用计数。*请勿直接使用*。参见page_ref.h */
    atomic_t _refcount;

#ifdef CONFIG_MEMCG
    struct mem_cgroup *mem_cgroup;
#endif

   /*
     * 在所有RAM都映射到内核地址空间的机器上，
     * 我们可以直接计算虚拟地址。在具有高端内存的机器上，
     * 有些内存是动态映射到内核虚拟内存的，
     * 所以我们需要一个地方来存储该地址。
     * 注意，在x86上这个字段可能是16位... ;)
     *
     * 乘法运算慢的体系结构可以在asm/page.h中
     * 定义WANT_PAGE_VIRTUAL
     */
#if defined(WANT_PAGE_VIRTUAL)
    void *virtual;            /* 内核虚拟地址（如果没有
                               映射，即高端内存，则为NULL）*/
#endif /* WANT_PAGE_VIRTUAL */

#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
    int _last_cpupid;
#endif
} _struct_page_alignment;
